package models

import com.workday.gridsimulator._
import akka.actor._
import akka.util.duration._
import play.api._
import play.api.libs.json._
import play.api.libs.iteratee._
import play.api.libs.concurrent._
import akka.util.Timeout
import akka.pattern.ask
import play.api.Play.current
//import com.workday.gridsimulator.SimulationStatus

object SimStatus {

  implicit val timeout = Timeout(50 seconds)

  lazy val simStatusActor = {
    val simStatusActor = Akka.system.actorOf(Props[SimStatus])

    simStatusActor
  }

  def start(gridSim: ActorRef, args: Array[String], updateAfterThisManyCompletions: Int): Promise[(Iteratee[JsValue, _], Enumerator[JsValue])] = {
    (simStatusActor ? Startme).asPromise.map {

      case Started(enumerator) =>
        val iteratee = Iteratee.foreach[JsValue] { event =>
          simStatusActor ! event 
        }

        gridSim ! RegMeForUpdates(simStatusActor, updateAfterThisManyCompletions)
        gridSim ! StartSim(args)
        (iteratee, enumerator)
    }
  }
}

class SimStatus extends Actor {

  var socketChannel: PushEnumerator[JsValue] = _
  var iteratee: Iteratee[JsValue, _] = _
  var grid: ActorRef = null

  override def receive = {
    case Startme =>
      // Create an Enumerator to write to this socket
      socketChannel = Enumerator.imperative[JsValue]()
      sender ! Started(socketChannel)

    case SimulationStatus(submittedWorkCount, workCompletedCount, currentTime, workerCount) =>
      val msg = JsObject(
        Seq(
          "submittedWorkCount" -> JsString(submittedWorkCount.toString),
          "workCompletedCount" -> JsString(workCompletedCount.toString),
          "currentTime" -> JsString(currentTime),
          "workerCount" -> JsString(workerCount.toString)))
          
      socketChannel.push(msg)
      grid = sender
      
    case Msg(txt) =>
      val msg = JsObject(
        Seq(
          "submittedWorkCount" -> JsString(txt)))
      socketChannel.push(msg)
      
    case SimFinished(result, cost) =>
      //println("CLOSING SOCKET")
      val msg = JsObject( Seq( "simulationResult" -> JsString(result), "cost" -> JsString(cost)))
      socketChannel.push(msg)
      //socketChannel.close EOF possibly first
      
    case JsString("STOP") =>
      if (grid != null) {
        println("stopping sim from webpage")
        grid ! UserSaidStop
      }
      
    case JsString("ADDWORKER") =>
      if (grid != null) {
        println("user added a worker")
        grid ! UserSaidAddWorker
      }
      
    case JsString("REMOVEWORKER") =>
      if (grid != null) {
        println("user said to remove a worker")
        grid ! UserSaidRemoveWorker
      }
      
    //arrival rate inc + dec msgs.....
    case JsString("INCREMENTARRIVALRATE") =>
      grid ! UserSaidIncrementArrivalRate
      
    case JsString("DECREMENTARRIVALRATE") =>
      grid ! UserSaidDecrementArrivalRate
      
    case _ =>
      println(sender + " sent msg to simstatus")
  }
}

case class Started(enumerator: Enumerator[JsValue])
case class SocketPush(data: JsObject)
case class Startme
